﻿using DotNetCommon;
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using DotNetCommon.Extensions;
using System.Threading.Tasks;
using DotNetCommon.Data;
using System.Linq;

namespace DBUtil.Builders;

/// <summary>
/// 查询构建器
/// </summary>
public class SelectBuilder<T> : SelectBuilderBase where T : class, new()
{
    internal SelectBuilder(DBAccess db, string alias, bool isAssign) : base(db)
    {
        EntityAliases = [new EntityAlias { EntityInfo = db.GetEntityInfoInternal<T>(true), Alias = alias, IsAssign = isAssign }];
    }
    #region AsTable
    public override SelectBuilder<T> AsTable(Func<string, string> func) => base.AsTable(func) as SelectBuilder<T>;
    public override SelectBuilder<T> AsTableIf(bool condition, Func<string, string> func) => base.AsTableIf(condition, func) as SelectBuilder<T>;
    public override SelectBuilder<T> AsTable(Action<TableName> func) => base.AsTable(func) as SelectBuilder<T>;
    public override SelectBuilder<T> AsTableIf(bool condition, Action<TableName> func) => base.AsTableIf(condition, func) as SelectBuilder<T>;
    #endregion

    #region 复写 CommandTimeout
    public override SelectBuilder<T> CommandTimeout(int timeoutSeconds)
        => base.CommandTimeout(timeoutSeconds) as SelectBuilder<T>;
    public override SelectBuilder<T> CommandTimeoutIf(bool condition, int timeoutSeconds)
        => condition ? CommandTimeout(timeoutSeconds) : this;
    #endregion

    #region Alias
    public new SelectBuilder<T> Alias(string alias) => base.Alias(alias) as SelectBuilder<T>;
    #endregion

    #region 过滤
    public SelectBuilder<T> Where(Expression<Func<T, bool>> expression) => base.Where(expression) as SelectBuilder<T>;
    public SelectBuilder<T> WhereIf(bool condition, Expression<Func<T, bool>> expression) => base.WhereIf(condition, expression) as SelectBuilder<T>;
    #endregion

    #region Distinct
    public new SelectBuilder<T> Distinct(bool isDistinct = true) => base.Distinct(isDistinct) as SelectBuilder<T>;
    #endregion

    #region 排序
    public SelectBuilder<T> OrderBy(Expression<Func<T, object>> expression) => base.OrderBy(expression) as SelectBuilder<T>;
    public SelectBuilder<T> OrderByIf(bool condition, Expression<Func<T, object>> expression) => condition ? OrderBy(expression) : this;

    public SelectBuilder<T> OrderByDesc(Expression<Func<T, object>> expression) => base.OrderByDesc(expression) as SelectBuilder<T>;
    public SelectBuilder<T> OrderByDescIf(bool condition, Expression<Func<T, object>> expression) => condition ? OrderByDesc(expression) : this;

    /// <summary>
    /// 排序,如: select.Order("age desc,id")
    /// </summary>
    public new SelectBuilder<T> Order(string orderSeg) => base.Order(orderSeg) as SelectBuilder<T>;
    #endregion

    #region 分组
    public GroupBuilder<T, TGroupKey> GroupBy<TGroupKey>(Expression<Func<T, TGroupKey>> expression)
    {
        return new GroupBuilder<T, TGroupKey>(this, expression);
    }
    #endregion

    #region 分页
    public new SelectBuilder<T> Page(int pageIndex, int pageSize) => base.Page(pageIndex, pageSize) as SelectBuilder<T>;
    public new SelectBuilder<T> Limit(int size) => base.Limit(size) as SelectBuilder<T>;
    public new SelectBuilder<T> Limit(int startIndex, int size) => base.Limit(startIndex, size) as SelectBuilder<T>;
    #endregion

    #region WithSql
    public new SelectBuilder<T> WithSql(string fromSql) => base.WithSql(fromSql) as SelectBuilder<T>;
    #endregion

    #region ToSql
    /// <summary>
    /// 专供 builder 解析使用, 提供外部已有 midValues
    /// </summary>
    internal string ToSqlFirstInternal(LambdaExpression expression, Dictionary<ParameterExpression, object> midValues, IEnumerable<KeyValuePair<ParameterExpression, string>> aliases)
    {
        this.Limit(1);
        return base.ToSql(EnumSelectToSql.ToList, expression, midValues, aliases);
    }
    /// <summary>
    /// 专供 builder 解析使用, 提供外部已有 midValues
    /// </summary>
    internal string ToSqlInternal(EnumSelectToSql enumSelectToSql, LambdaExpression expression, Dictionary<ParameterExpression, object> midValues, IEnumerable<KeyValuePair<ParameterExpression, string>> aliases)
    {
        return base.ToSql(enumSelectToSql, expression, midValues, aliases);
    }

    private List<(string dtoCol, EntityPropertyInfo entPropInfo)> GetDtoEntMap<Dto>()
    {
        var dtoEntityInfo = db.GetEntityInfoInternal<Dto>();
        var entityInfo = db.GetEntityInfoInternal<T>();
        var allProps = entityInfo.Props.Where(i => !i.IsIgnoreSelect).Select(i => i.PropNamePure).ToList();
        var dtoMap = dtoEntityInfo.Props.Where(i => allProps.Contains(i.PropNamePure)).Select(i =>
        {
            var entPropInfo = entityInfo.Props.First(j => j.PropNamePure == i.PropNamePure);
            return (i.PropNameQuoted, entPropInfo);
        }).ToList();
        return dtoMap;
    }

    //ToSqlFirst
    public string ToSqlFirst() => base.ToSqlFirst(null);
    public string ToSqlFirst<Dto>(Expression<Func<T, Dto>> expression = null)
    {
        if (expression != null) return base.ToSqlFirst(expression);
        var dtoMap = GetDtoEntMap<Dto>();
        return base.ToSqlFirstDto(dtoMap);
    }
    //ToSql
    public string ToSql() => base.ToSqlList(null);
    //ToSqlList
    public string ToSqlList() => base.ToSqlList(null);

    internal string ToSqlSelectClause() => base.ToSqlSelectClause(null);
    internal override SelectBuilder<T> AddExtraSelectSeg(string seg) => base.AddExtraSelectSeg(seg) as SelectBuilder<T>;

    public string ToSqlList<Dto>(Expression<Func<T, Dto>> expression = null)
    {
        if (expression != null) return base.ToSqlList(expression);
        var dtoMap = GetDtoEntMap<Dto>();
        return base.ToSqlListDto(dtoMap);
    }
    //ToSqlPage
    public string ToSqlPage() => base.ToSqlPage(null);
    public string ToSqlPage(int pageIndex, int pageSize) => Page(pageIndex, pageSize).ToSqlPage(null);
    public string ToSqlPage<Dto>(Expression<Func<T, Dto>> expression = null)
    {
        if (expression != null) return base.ToSqlPage(expression);
        var dtoMap = GetDtoEntMap<Dto>();
        return base.ToSqlPageDto(dtoMap);
    }
    public string ToSqlPage<Dto>(int pageIndex, int pageSize)
    {
        this.Page(pageIndex, pageSize);
        var dtoMap = GetDtoEntMap<Dto>();
        return base.ToSqlPageDto(dtoMap);
    }
    public string ToSqlPage<Dto>(Expression<Func<T, Dto>> expression, int pageIndex, int pageSize)
        => Page(pageIndex, pageSize).ToSqlPage(expression);

    public string ToSqlMax<Dto>(Expression<Func<T, Dto>> expression) => base.ToSql(EnumSelectToSql.Max, expression);
    public string ToSqlMin<Dto>(Expression<Func<T, Dto>> expression) => base.ToSql(EnumSelectToSql.Min, expression);
    public string ToSqlAvg<Dto>(Expression<Func<T, Dto>> expression) => base.ToSql(EnumSelectToSql.Avg, expression);
    public string ToSqlSum<Dto>(Expression<Func<T, Dto>> expression) => base.ToSql(EnumSelectToSql.Sum, expression);
    #endregion

    #region 聚合 & 聚合sql
    #region 聚合Sql
    public string MaxSql<TKey>(Expression<Func<T, TKey>> expression) => base.MaxSql<TKey>(expression);
    public string MinSql<TKey>(Expression<Func<T, TKey>> expression) => base.MinSql<TKey>(expression);
    public string SumSql<TKey>(Expression<Func<T, TKey>> expression) => base.SumSql<TKey>(expression);
    public string AvgSql<TKey>(Expression<Func<T, TKey>> expression) => base.AvgSql<TKey>(expression);
    #endregion

    #region 聚合同步
    public TKey MaxOrDefault<TKey>(Expression<Func<T, TKey>> expression) => base.MaxOrDefault<TKey>(expression);
    public TKey MinOrDefault<TKey>(Expression<Func<T, TKey>> expression) => base.MinOrDefault<TKey>(expression);
    public TKey SumOrDefault<TKey>(Expression<Func<T, TKey>> expression) => base.SumOrDefault<TKey>(expression);
    public TKey AvgOrDefault<TKey>(Expression<Func<T, TKey>> expression) => base.AvgOrDefault<TKey>(expression);
    #endregion

    #region 聚合异步
    public async Task<TKey> MaxOrDefaultAsync<TKey>(Expression<Func<T, TKey>> expression) => await base.MaxOrDefaultAsync<TKey>(expression);
    public async Task<TKey> MinOrDefaultAsync<TKey>(Expression<Func<T, TKey>> expression) => await base.MinOrDefaultAsync<TKey>(expression);
    public async Task<TKey> SumOrDefaultAsync<TKey>(Expression<Func<T, TKey>> expression) => await base.SumOrDefaultAsync<TKey>(expression);
    public async Task<TKey> AvgOrDefaultAsync<TKey>(Expression<Func<T, TKey>> expression) => await base.AvgOrDefaultAsync<TKey>(expression);
    #endregion
    #endregion

    #region 执行
    #region 同步
    //First
    //1. entity
    public T First()
    {
        var sql = this.Limit(1).ToSqlFirst();
        var model = db.SelectModel<T>(sql);
        return model;
    }
    //2. dto lambda
    public Dto First<Dto>(Expression<Func<T, Dto>> expression)
    {
        var sql = ToSqlFirst<Dto>(expression);
        var model = db.SelectModel<Dto>(sql);
        return model;
    }
    //3. dto direct
    public Dto First<Dto>()
    {
        var sql = this.Limit(1).ToSqlFirst<Dto>();
        var model = db.SelectModel<Dto>(sql);
        return model;
    }

    //FirstOrDefault
    //1. entity
    public T FirstOrDefault()
    {
        var sql = this.Limit(1).ToSqlFirst();
        var model = db.SelectModelOrDefault<T>(sql);
        return model;
    }
    //2. dto lambda
    public Dto FirstOrDefault<Dto>(Expression<Func<T, Dto>> expression)
    {
        var sql = ToSqlFirst<Dto>(expression);
        var model = db.SelectModelOrDefault<Dto>(sql);
        return model;
    }
    //3. dto direct
    public Dto FirstOrDefault<Dto>()
    {
        var sql = this.Limit(1).ToSqlFirst<Dto>();
        var model = db.SelectModelOrDefault<Dto>(sql);
        return model;
    }

    //ToList
    //1. entity
    public List<T> ToList()
    {
        var sql = this.ToSqlList();
        var list = db.SelectModelList<T>(sql);
        return list;
    }
    //2. dto lambda
    public List<Dto> ToList<Dto>(Expression<Func<T, Dto>> expression)
    {
        var sql = ToSqlList<Dto>(expression);
        var list = db.SelectModelList<Dto>(sql);
        return list;
    }
    //3. dto direct
    public List<Dto> ToList<Dto>()
    {
        var sql = this.ToSqlList<Dto>();
        var list = db.SelectModelList<Dto>(sql);
        return list;
    }

    //ToPage
    //1. entity
    public Page<T> ToPage()
    {
        var sql = ToSql(EnumSelectToSql.ToPage);
        return SelectPage<T>(db, sql);
    }
    //1. entity-pageparam
    public Page<T> ToPage(int pageIndex, int pageSize)
    {
        var sql = Page(pageIndex, pageSize).ToSql(EnumSelectToSql.ToPage);
        return SelectPage<T>(db, sql);
    }
    //2. dto lambda
    public Page<Dto> ToPage<Dto>(Expression<Func<T, Dto>> expression) => base.ToPage<Dto>(expression);
    //2. dto lambda-pageparam
    public Page<Dto> ToPage<Dto>(Expression<Func<T, Dto>> expression, int pageIndex, int pageSize) => base.ToPage<Dto>(expression, pageIndex, pageSize);
    //3. dto direct
    public Page<Dto> ToPage<Dto>()
    {
        var sql = this.ToSqlPage<Dto>();
        return SelectPage<Dto>(db, sql);
    }
    //3. dto direct-pageparam
    public Page<Dto> ToPage<Dto>(int pageIndex, int pageSize)
    {
        var sql = this.Page(pageIndex, pageSize).ToSqlPage<Dto>();
        return SelectPage<Dto>(db, sql);
    }
    #endregion

    #region 异步
    //FirstAsync
    //1. entity
    public async Task<T> FirstAsync()
    {
        var sql = this.Limit(1).ToSqlFirst();
        var model = await db.SelectModelAsync<T>(sql);
        return model;
    }
    //2. dto lambda
    public async Task<Dto> FirstAsync<Dto>(Expression<Func<T, Dto>> expression)
        => await base.FirstAsync<Dto>(expression);
    //3. dto direct
    public async Task<Dto> FirstAsync<Dto>()
    {
        var sql = this.Limit(1).ToSqlFirst<Dto>();
        var model = await db.SelectModelAsync<Dto>(sql);
        return model;
    }

    //FirstOrDefaultAsync
    //1. entity
    public async Task<T> FirstOrDefaultAsync()
    {
        var sql = this.Limit(1).ToSqlFirst();
        var model = await db.SelectModelOrDefaultAsync<T>(sql);
        return model;
    }
    //2. dto lambda
    public async Task<Dto> FirstOrDefaultAsync<Dto>(Expression<Func<T, Dto>> expression)
        => await base.FirstOrDefaultAsync<Dto>(expression);
    //3. dto direct
    public async Task<Dto> FirstOrDefaultAsync<Dto>()
    {
        var sql = this.Limit(1).ToSqlFirst<Dto>();
        var model = await db.SelectModelOrDefaultAsync<Dto>(sql);
        return model;
    }

    //ToListAsync
    //1. entity
    public async Task<List<T>> ToListAsync()
    {
        var sql = ToSql(EnumSelectToSql.ToList, null);
        var list = await db.SelectModelListAsync<T>(sql);
        return list;
    }
    //2. dto lambda
    public async Task<List<Dto>> ToListAsync<Dto>(Expression<Func<T, Dto>> expression) => await base.ToListAsync<Dto>(expression);
    //3. dto direct
    public async Task<List<Dto>> ToListAsync<Dto>()
    {
        var sql = ToSqlList<Dto>();
        var list = await db.SelectModelListAsync<Dto>(sql);
        return list;
    }

    //ToPageAsync
    //1. entity
    public async Task<Page<T>> ToPageAsync()
    {
        var sql = ToSqlPage();
        return await SelectPageAsync<T>(db, sql);
    }
    //1. entity-pageparam
    public async Task<Page<T>> ToPageAsync(int pageIndex, int pageSize) => await this.Page(pageIndex, pageSize).ToPageAsync<T>();
    //2. dto lambda
    public async Task<Page<Dto>> ToPageAsync<Dto>(Expression<Func<T, Dto>> expression) => await base.ToPageAsync<Dto>(expression);
    //2. dto lambda-param
    public async Task<Page<Dto>> ToPageAsync<Dto>(Expression<Func<T, Dto>> expression, int pageIndex, int pageSize) => await base.ToPageAsync<Dto>(expression, pageIndex, pageSize);
    //3. dto direct
    public async Task<Page<Dto>> ToPageAsync<Dto>()
    {
        var sql = ToSqlPage<Dto>();
        return await SelectPageAsync<Dto>(db, sql);
    }
    //3. dto direct-param
    public async Task<Page<Dto>> ToPageAsync<Dto>(int pageIndex, int pageSize)
    {
        var sql = this.Page(pageIndex, pageSize).ToSqlPage<Dto>();
        return await SelectPageAsync<Dto>(db, sql);
    }
    #endregion
    #endregion

    #region WhereSeg
    public override SelectBuilder<T> WhereSeg<TAny>(Expression<Func<TAny, bool>> filter)
        => base.WhereSeg(filter) as SelectBuilder<T>;
    public override SelectBuilder<T> WhereSegIf<TAny>(bool condition, Expression<Func<TAny, bool>> filter)
        => base.WhereSegIf(condition, filter) as SelectBuilder<T>;
    public override SelectBuilder<T> WhereSeg<TAny, TAny2>(Expression<Func<TAny, TAny2, bool>> filter)
        => base.WhereSeg(filter) as SelectBuilder<T>;
    public override SelectBuilder<T> WhereSegIf<TAny, TAny2>(bool condition, Expression<Func<TAny, TAny2, bool>> filter)
        => base.WhereSegIf(condition, filter) as SelectBuilder<T>;
    public override SelectBuilder<T> WhereSeg<TAny, TAny2, TAny3>(Expression<Func<TAny, TAny2, TAny3, bool>> filter)
         => base.WhereSeg(filter) as SelectBuilder<T>;
    public override SelectBuilder<T> WhereSegIf<TAny, TAny2, TAny3>(bool condition, Expression<Func<TAny, TAny2, TAny3, bool>> filter)
        => base.WhereSegIf(condition, filter) as SelectBuilder<T>;
    public override SelectBuilder<T> WhereSeg<TAny, TAny2, TAny3, TAny4>(Expression<Func<TAny, TAny2, TAny3, TAny4, bool>> filter)
        => base.WhereSeg(filter) as SelectBuilder<T>;
    public override SelectBuilder<T> WhereSegIf<TAny, TAny2, TAny3, TAny4>(bool condition, Expression<Func<TAny, TAny2, TAny3, TAny4, bool>> filter)
         => base.WhereSegIf(condition, filter) as SelectBuilder<T>;
    #endregion
    #region Where
    public override SelectBuilder<T> Where(string filter) => base.Where(filter) as SelectBuilder<T>;
    public override SelectBuilder<T> WhereIf(bool condition, string filter) => base.WhereIf(condition, filter) as SelectBuilder<T>;
    #endregion

    #region Join
    public SelectBuilder<T, T2> LeftJoin<T2>(Expression<Func<T, T2, bool>> joinExp) where T2 : class, new()
    {
        return new SelectBuilder<T, T2>(this, (joinExp, EnumJoinType.LeftJoin), BuilderHelper.GetNewTableAlias("t2"), false);
    }
    public SelectBuilder<T, T2> LeftJoin<T2>(Expression<Func<T, T2, bool>> joinExp, string alias) where T2 : class, new()
    {
        bool isAssign = true;
        if (alias.IsNullOrWhiteSpace())
        {
            alias = BuilderHelper.GetNewTableAlias("t2");
            isAssign = false;
        }
        return new SelectBuilder<T, T2>(this, (joinExp, EnumJoinType.LeftJoin), alias, isAssign);
    }
    public SelectBuilder<T, T2> RightJoin<T2>(Expression<Func<T, T2, bool>> joinExp) where T2 : class, new()
    {
        return new SelectBuilder<T, T2>(this, (joinExp, EnumJoinType.RightJoin), BuilderHelper.GetNewTableAlias("t2"), false);
    }
    public SelectBuilder<T, T2> RightJoin<T2>(Expression<Func<T, T2, bool>> joinExp, string alias) where T2 : class, new()
    {
        bool isAssign = true;
        if (alias.IsNullOrWhiteSpace())
        {
            alias = BuilderHelper.GetNewTableAlias("t2");
            isAssign = false;
        }
        return new SelectBuilder<T, T2>(this, (joinExp, EnumJoinType.RightJoin), alias, isAssign);
    }
    public SelectBuilder<T, T2> InnerJoin<T2>(Expression<Func<T, T2, bool>> joinExp) where T2 : class, new()
    {
        return new SelectBuilder<T, T2>(this, (joinExp, EnumJoinType.InnerJoin), BuilderHelper.GetNewTableAlias("t2"), false);
    }
    public SelectBuilder<T, T2> InnerJoin<T2>(Expression<Func<T, T2, bool>> joinExp, string alias) where T2 : class, new()
    {
        bool isAssign = true;
        if (alias.IsNullOrWhiteSpace())
        {
            alias = BuilderHelper.GetNewTableAlias("t2");
            isAssign = false;
        }
        return new SelectBuilder<T, T2>(this, (joinExp, EnumJoinType.InnerJoin), alias, isAssign);
    }
    public SelectBuilder<T, T2> CrossJoin<T2>(Expression<Func<T, T2, bool>> joinExp) where T2 : class, new()
    {
        return new SelectBuilder<T, T2>(this, (joinExp, EnumJoinType.CrossJoin), BuilderHelper.GetNewTableAlias("t2"), false);
    }
    public SelectBuilder<T, T2> CrossJoin<T2>(Expression<Func<T, T2, bool>> joinExp, string alias) where T2 : class, new()
    {
        bool isAssign = true;
        if (alias.IsNullOrWhiteSpace())
        {
            alias = BuilderHelper.GetNewTableAlias("t2");
            isAssign = false;
        }
        return new SelectBuilder<T, T2>(this, (joinExp, EnumJoinType.CrossJoin), alias, isAssign);
    }
    #endregion

    #region 转到insert
    public InsertFromSelectBuilder<TInsert> AsInsert<TInsert>(Expression<Func<T, TInsert>> expression) where TInsert : class, new()
    {
        var names = ExpressionHelper.GetInitOrReturnPropNames(expression).ToArray();
        if (names.IsNullOrEmpty()) throw new Exception($"不能在 AsInsert 方法中使用 \"i=>i\" 这种形式的表达式!");
        return new InsertFromSelectBuilder<TInsert>(db, this, expression, names);
    }
    public InsertFromSelectBuilder<T> AsInsert(Expression<Func<T, T>> expression = null)
    {
        var names = ExpressionHelper.GetInitOrReturnPropNames(expression).ToArray();
        if (names.IsNullOrEmpty()) names = db.GetEntityInfoInternal<T>().Props.Where(i => !i.IsIgnoreInsert).Select(i => i.PropNamePure).ToArray();
        return new InsertFromSelectBuilder<T>(db, this, expression, names);
    }
    #endregion
}

/// <summary>
/// 分组查询构建器
/// </summary>
public class GroupBuilder<T, TGroupKey> : GroupBuilderBase where T : class, new()
{
    internal GroupBuilder(SelectBuilder<T> selectBuilder, Expression<Func<T, TGroupKey>> groupExpression) : base(selectBuilder, groupExpression) { }

    #region 过滤
    public GroupBuilder<T, TGroupKey> Having(Expression<Func<IGroupFilter<T, TGroupKey>, bool>> expression)
    {
        AssertUtil.NotNull(expression);
        havings.Add(expression);
        return this;
    }
    public GroupBuilder<T, TGroupKey> HavingIf(bool condition, Expression<Func<IGroupFilter<T, TGroupKey>, bool>> expression)
        => condition ? Having(expression) : this;

    public GroupBuilder<T, TGroupKey> HavingRaw(string filter)
    {
        AssertUtil.NotNull(filter);
        havings.Add(filter);
        return this;
    }
    public GroupBuilder<T, TGroupKey> HavingRawIf(bool condition, string filter)
        => condition ? HavingRaw(filter) : this;
    #endregion

    #region 分页
    public override GroupBuilder<T, TGroupKey> Page(int pageIndex, int pageSize)
        => base.Page(pageIndex, pageSize) as GroupBuilder<T, TGroupKey>;
    public override GroupBuilder<T, TGroupKey> Limit(int size)
        => base.Limit(size) as GroupBuilder<T, TGroupKey>;
    public override GroupBuilder<T, TGroupKey> Limit(int startIndex, int size)
        => base.Limit(size, startIndex) as GroupBuilder<T, TGroupKey>;
    #endregion

    #region ToSql
    public string ToSqlFirst<Dto>(Expression<Func<IGroupFilter<T, TGroupKey>, Dto>> expression)
        => base.ToSqlFirst(expression);
    public string ToSqlList<Dto>(Expression<Func<IGroupFilter<T, TGroupKey>, Dto>> expression)
        => base.ToSqlList(expression);
    public string ToSqlPage<Dto>(Expression<Func<IGroupFilter<T, TGroupKey>, Dto>> expression)
        => base.ToSqlPage(expression);
    public string ToSqlPage<Dto>(Expression<Func<IGroupFilter<T, TGroupKey>, Dto>> expression, int pageIndex, int pageSize)
        => base.ToSqlPage(expression, pageIndex, pageSize);
    #endregion

    #region 排序
    public GroupBuilder<T, TGroupKey> OrderBy(Expression<Func<IGroupFilter<T, TGroupKey>, object>> expression)
        => base.OrderBy(expression) as GroupBuilder<T, TGroupKey>;

    public GroupBuilder<T, TGroupKey> OrderByDesc(Expression<Func<IGroupFilter<T, TGroupKey>, object>> expression)
        => base.OrderByDesc(expression) as GroupBuilder<T, TGroupKey>;

    /// <summary>
    /// 排序,如: select.Order("age desc,id")
    /// </summary>
    public override GroupBuilder<T, TGroupKey> Order(string orderSeg)
        => base.Order(orderSeg) as GroupBuilder<T, TGroupKey>;
    #endregion

    #region 执行
    #region 同步
    public Dto First<Dto>(Expression<Func<IGroupFilter<T, TGroupKey>, Dto>> expression)
        => base.First<Dto>(expression);
    public Dto FirstOrDefault<Dto>(Expression<Func<IGroupFilter<T, TGroupKey>, Dto>> expression)
        => base.FirstOrDefault<Dto>(expression);
    public List<Dto> ToList<Dto>(Expression<Func<IGroupFilter<T, TGroupKey>, Dto>> expression)
        => base.ToList<Dto>(expression);
    public Page<Dto> ToPage<Dto>(Expression<Func<IGroupFilter<T, TGroupKey>, Dto>> expression)
        => base.ToPage<Dto>(expression);
    public Page<Dto> ToPage<Dto>(Expression<Func<IGroupFilter<T, TGroupKey>, Dto>> expression, int pageIndex, int pageSize)
        => base.ToPage<Dto>(expression, pageIndex, pageSize);
    #endregion
    #region 异步
    public async Task<Dto> FirstAsnc<Dto>(Expression<Func<IGroupFilter<T, TGroupKey>, Dto>> expression)
        => await base.FirstAsnc<Dto>(expression);
    public async Task<Dto> FirstOrDefaultAsnc<Dto>(Expression<Func<IGroupFilter<T, TGroupKey>, Dto>> expression)
        => await base.FirstOrDefaultAsnc<Dto>(expression);
    public async Task<List<Dto>> ToListAsync<Dto>(Expression<Func<IGroupFilter<T, TGroupKey>, Dto>> expression)
        => await base.ToListAsync<Dto>(expression);
    public async Task<Page<Dto>> ToPageAsync<Dto>(Expression<Func<IGroupFilter<T, TGroupKey>, Dto>> expression)
        => await base.ToPageAsync<Dto>(expression);
    public async Task<Page<Dto>> ToPageAsync<Dto>(Expression<Func<IGroupFilter<T, TGroupKey>, Dto>> expression, int pageIndex, int pageSize)
        => await base.ToPageAsync<Dto>(expression, pageIndex, pageSize);
    #endregion
    #endregion
}

public interface IGroupFilter<T, TGroupKey> where T : class, new()
{
    public TGroupKey Key { get; }
    public int Length { get; }

    #region 聚合函数 sum max min avg
    //https://dev.mysql.com/doc/refman/8.0/en/aggregate-functions.html
    public TKey Max<TKey>(Func<T, TKey> expression);
    public TKey Min<TKey>(Func<T, TKey> expression);
    public TKey Sum<TKey>(Func<T, TKey> expression);
    public TKey Avg<TKey>(Func<T, TKey> expression);
    #endregion

    #region 字符串 Join
    /// <summary>
    /// string_agg(sqlserver/postgresql) group_concat(mysql)
    /// <list type="bullet">
    /// <item>mysql: <seealso href="https://dev.mysql.com/doc/refman/8.0/en/aggregate-functions.html#function_group-concat"/></item>
    /// <item>sqlserver: <seealso href="https://learn.microsoft.com/zh-cn/sql/t-sql/functions/string-agg-transact-sql?view=sql-server-ver15"/></item>
    /// </list>
    /// </summary>
    /// <remarks>注意: 仅mysql支持 distinct</remarks>
    public string Join(Func<T, string> expression, string separator, Func<T, object> orderBy, bool desc, bool isDistinct);
    /// <summary>
    /// string_agg(sqlserver/postgresql) group_concat(mysql)
    /// <list type="bullet">
    /// <item>mysql: <seealso href="https://dev.mysql.com/doc/refman/8.0/en/aggregate-functions.html#function_group-concat"/></item>
    /// <item>sqlserver: <seealso href="https://learn.microsoft.com/zh-cn/sql/t-sql/functions/string-agg-transact-sql?view=sql-server-ver15"/></item>
    /// </list>
    /// </summary>
    public string Join(Func<T, string> expression, string separator, Func<T, object> orderBy, bool desc);
    /// <summary>
    /// string_agg(sqlserver/postgresql) group_concat(mysql)
    /// <list type="bullet">
    /// <item>mysql: <seealso href="https://dev.mysql.com/doc/refman/8.0/en/aggregate-functions.html#function_group-concat"/></item>
    /// <item>sqlserver: <seealso href="https://learn.microsoft.com/zh-cn/sql/t-sql/functions/string-agg-transact-sql?view=sql-server-ver15"/></item>
    /// </list>
    /// </summary>
    public string Join(Func<T, string> expression, string separator);
    #endregion
}
